昨天的章節,我們實作了 Recreate
,Rolling update
兩個部署策略。今天來繼續實作剩下的策略。
在 Kubernetes 中,可以使用兩個具有相同 Pod 標籤的 Deployment 來實現 Canary
(金絲雀)部署。新版本的副本和舊版本的一起發佈。在一段時間後如果沒有檢測到錯誤,則可以擴展新版本的副本數量並刪除舊版本的應用。
如果想要透過 Kubernetes 原生方法實作金絲雀
部署,只能透過兩個版本副本比例來調整流量版分比,例如當 v1 與 v2 副本數都是 10 個時,流量比例即 1:1。如果你需要更精確的控制策略,建議使用 Service Mesh 服務 (如 Istio),或是透過 Ingress Controller 服務 (如 nginx)。
下面我們會透過原生 Kubernetes 實作 Canary
部署策略 。
組態檔案: my-app-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
labels:
app: my-app-svc
spec:
type: NodePort
selector:
app: my-app
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30000
組態檔案: my-app-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
spec:
replicas: 10
selector:
matchLabels:
app: my-app
version: v1
strategy:
type: Recreate
template:
metadata:
name: my-app
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: lofairy/foo
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案: my-app-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2
strategy:
type: Recreate
template:
metadata:
name: my-app
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: lofairy/bar
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案大致上與 Blue/Green
練習使用的一致,唯一的差別是 my-app-svc.yaml
中的 label select 去掉了標籤 version
,因此 Service 會同時適用 v1 和 v2 的 Pod 應用。
接下來我們按照下面的步驟來驗證 Canary
策略:
kubectl apply -f my-app-v1.yaml my-app-svc.yaml
my-app
是否部署成功`curl localhost:30000
---
{
"message": "foo"
}
可以看到 v1 的應用正常運行了
為了查看接下來流量切換的變化,我們進行以下動作:
t1
,運行以下命令來持續訪問 Servicewhile sleep 0.1; do curl http://0.0.0.0:30000; echo ""; done
kubectl apply -f my-app-v2.yaml
現在,叢集中同時有 my-app-v1
, my-app-v2
兩個版本的應用
t1
,查看結果{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
可以看到大致上有不到 10% 的流量會訪問到 v2 應用,代表金絲雀部署成功了
kubectl scale --replicas=10 deploy my-app-v2
t1
,查看結果{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"bar"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
可以看到 v1 與 v2 版本的流量大致是 1:1。
最後,如果 v2 版本沒有問題,就可以刪除舊版的 Deployment:
kubectl delete -f my-app-v1.yaml
從上面的過程可以理解 Canary
部署有以下特性:
如果對新功能的發佈沒有信心,使用金絲雀部署是不錯的選擇。
在 Kubernetes 中,我們可以用兩種方法來實現 Blue/Green
(藍綠)部署,通過單個 Service 對象或者 Ingress 控製器來實現藍綠部署,實際操作都是類似的,都是通過 label 標籤去控制。
下面我們會透過原生 Kubernetes Service 實作 Blue/Green
部署策略 。
組態檔案: my-app-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
labels:
app: my-app-svc
spec:
type: NodePort
selector:
app: my-app
version: v1
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30000
組態檔案: my-app-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
name: my-app
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: lofairy/foo
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案: my-app-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
name: my-app
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: lofairy/bar
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案大致上與 Recreate
練習使用的一致,唯一的差別是 v1 (藍), v2 (綠) Pod 增加了新的標籤 version
,用以區別不同版本的應用。因為 Blue/Green
部署的過程不改變流量切換,我們依然使用 sepc.strategy.type=Recreate
策略加快副本 scale up/down 的速度。
接下來我們按下面的步驟來驗證 Blue/Green
策略:
kubectl apply -f my-app-v1.yaml my-app-svc.yaml
my-app
是否部署成功,訪問 localhost:30000
,得到以下回應curl localhost:30000
---
{
"message": "foo"
}
可以看到 v1 的應用正常運行了
kubectl apply -f my-app-v2.yaml
現在,叢集中同時有 my-app-v1
, my-app-v2
兩個版本的應用,不過 Service 目前將流量都指向 my-app-v1
裡。
為了查看接下來流量切換的變化,我們進行以下動作:
t1
,運行以下命令來持續訪問 Servicewhile sleep 0.1; do curl http://0.0.0.0:30000; echo ""; done
kubectl patch service my-app-svc -p '{"spec":{"selector":{"version":"v2"}}}'
t1
,查看結果{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"foo"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
{"message":"bar"}
可以看到流量在一瞬間切換成 v2 版本,並沒有中斷服務。
如果 v2 版本發生問題,可以再次修改 Service 回滾:
kubectl patch service my-app-svc -p '{"spec":{"selector":{"version":"v1"}}}'
最後,如果 v2 版本沒有問題,就可以刪除舊版的 Deployment:
kubectl delete -f my-app-v1.yaml
從上面的過程可以理解 Blue/Green
部署有以下特性:
在生產環境執行s無法向下相容的更新部署,Blue/Green
是很好的部署策略。
在原生 Kubernetes 中,並無實作 A/B testing,不過可以透過 Service Mesh 服務 (如 Istio) 或其他 Ingress Controller 服務 (如 nginx) 實現。
下面我們會透過 Nginx Ingress Controller 實作 A/B Testing
部署策略 。
我們需要在叢集中安裝 Nginx Ingress Controller。
之前的章節中已經帶領過大家如何安裝和使用 Nginx Ingress Controller,這邊就不再贅述。
組態檔案: my-app-v1.yaml
---
apiVersion: v1
kind: Service
metadata:
name: my-app-v1-svc
labels:
app: my-app-v1-svc
spec:
selector:
app: my-app
version: v1
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
strategy:
type: Recreate
template:
metadata:
name: my-app
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: lofairy/foo
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案: my-app-v2.yaml
---
apiVersion: v1
kind: Service
metadata:
name: my-app-v2-svc
labels:
app: my-app-v2-svc
spec:
selector:
app: my-app
version: v2
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v2
strategy:
type: Recreate
template:
metadata:
name: my-app
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: lofairy/bar
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
組態檔案: ingress.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30000
selector:
app.kubernetes.io/name: ingress-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/canary: "false"
spec:
ingressClassName: nginx
rules:
- host: "localhost"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-v1-svc
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: canary-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "version"
nginx.ingress.kubernetes.io/canary-by-header-value: "v2"
spec:
ingressClassName: nginx
rules:
- host: "localhost"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-v2-svc
port:
number: 8080
大致說明一下:
Canary
練習使用的一致,不過這次為兩個版本的應用都定義了一組 Service 供 Ingress 使用。Nginx Ingress Controller
的 Canary
拓展實現依據 header 分流。KinD
設定對本機暴露的 30000 port,我們在 Ingress 的外邊再加上一組 Service。接下來我們按照下面的步驟來驗證 A/B Testing
策略:
kubectl apply -f my-app-v1.yaml -f my-app-v2.yaml
kubectl apply -f ingress.yaml
my-app
和 ingress
是否部署成功curl localhost:30000
---
{"message": "foo"}
得到回應就代表部署成功。
不過訪問 ingress 只能得到 v1 版本的回應,是因為我們在組態檔案的定義是:
version: v2
時,導流到 v2 應用的 Service現在我們來試著在 Header 加入參數,再訪問 ingress:
curl -H "version: v2" http://localhost:30000
---
bar
可以看到,我們成功的以 Header 為條件將訪問者分流到不同版本的應用。
從上面的過程可以理解 A/B Testing
部署有以下特性:
在 Deployment strategy 三個章節中,我們已經對大部分的部署策略有了蠻深的了解。落實在實際應用中,我們需要分析產品和應用的需求,選擇合適的部署策略。沒有哪個部署策略是完美的。
Kubernetes 部署策略详解-阳明的博客|Kubernetes|Istio|Prometheus|Python|Golang|云原生 (qikqiak.com)